package com.zoro.barn.business.service.sqldocument.business.sql.controller;

import com.zoro.barn.business.service.sqldocument.business.sql.dto.ExportSqlDto;
import com.zoro.barn.business.service.sqldocument.business.sql.entity.BarnSqlInfo;
import com.zoro.barn.business.service.sqldocument.business.sql.service.BarnSqlInfoService;
import com.zoro.barn.commons.webcommons.tools.WebContentUtil;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author zhaoxingwu
 */
@RestController
@RequestMapping("/sqlExport")
public class BarnSqlExportController {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final BarnSqlInfoService barnSqlInfoService;

    public BarnSqlExportController(BarnSqlInfoService barnSqlInfoService) {
        this.barnSqlInfoService = barnSqlInfoService;
    }

    @RequestMapping
    public void export(ExportSqlDto sqlInfoDto) throws IOException {
        HttpServletResponse response = WebContentUtil.getResponse();
        List<BarnSqlInfo> sqlInfos;
        if (StringUtils.isNotBlank(sqlInfoDto.getProductId())) {

            BarnSqlInfo info = new BarnSqlInfo();
            info.setProductId(sqlInfoDto.getProductId());
            if (StringUtils.isBlank(sqlInfoDto.getProjectId())) {
                sqlInfos = this.barnSqlInfoService.getProductSql(sqlInfoDto.getProductId(), sqlInfoDto.getStart(), sqlInfoDto.getEnd());
            } else {
                sqlInfos = this.barnSqlInfoService.getProjectSql(sqlInfoDto.getProductId(), sqlInfoDto.getProjectId(), sqlInfoDto.getStart(), sqlInfoDto.getEnd());
            }
        } else {
            sqlInfos = new ArrayList<>();
        }
        if (CollectionUtils.isEmpty(sqlInfos)) {
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setHeader(HttpHeaders.CONNECTION, "close");
            response.setHeader("Content-Disposition", "attachment;filename=NOT_FOUND.sql");
            OutputStream os = response.getOutputStream();
            os.write("-- no one sql was found,please check!!".getBytes(StandardCharsets.UTF_8));
            os.flush();
            os.close();
        } else {
            try (OutputStream os = response.getOutputStream()) {
                Date now = new Date();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-hhmmss");
                String fileName = sdf.format(now) + ".zip";
                response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
                response.setHeader(HttpHeaders.CONNECTION, "close");
                response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

                Map<String, Set<String>> sqlIdMap = new HashMap<>();

                sqlInfos.forEach(v -> {
                    String schema = v.getSchemaName();
                    Set<String> sqlTexts = sqlIdMap.get(schema);
                    if (CollectionUtils.isEmpty(sqlTexts)) {
                        sqlTexts = new LinkedHashSet<>();
                        sqlIdMap.put(schema, sqlTexts);
                    }
                    try {
                        String sqlText = this.barnSqlInfoService.getSqlText(v.getDocumentId());

                        sqlText =
                                String.format("-- -- ID :%s , SQL ID: %s, SQL TITLE: %s ----------------\n", v.getPid(), v.getDocumentId(), v.getSqlTitle()) +
                                        sqlText + ";\n" +
                                        String.format("-- -- END HERE : %s ----------------\n\n\n", v.getPid());

                        sqlTexts.add(sqlText);
                    } catch (Exception e) {
                        logger.error("Can not get sql text , sql id {},text id {}, cause {}", v.getPid(), v.getDocumentId(), e.getMessage());
                        sqlTexts.add("-- can not get sql text, sql id " + v.getPid() + ", text id " + v.getDocumentId());
                    }
                });
                write(os, sqlIdMap);
            }
        }
    }

    private void write(OutputStream os, Map<String, Set<String>> sqlMap) throws IOException {

        if (sqlMap == null || sqlMap.size() == 0) {
            return;
        }

        try (ZipOutputStream zos = new ZipOutputStream(os)) {
            Set<Map.Entry<String, Set<String>>> entry = sqlMap.entrySet();
            for (Map.Entry<String, Set<String>> e : entry) {
                String zipEntryName = e.getKey() + ".sql";
                zos.putNextEntry(new ZipEntry(zipEntryName));
                for (String sql : e.getValue()) {
                    if (StringUtils.isNotBlank(sql)) {
                        zos.write(sql.getBytes(StandardCharsets.UTF_8));
                    }
                }
            }
            zos.closeEntry();
            zos.flush();
        }
    }
}
